package com.detooa.detooa.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.detooa.common.exception.ServiceException;
import com.detooa.common.utils.StringUtils;
import com.detooa.detooa.config.OaConfig;
import com.detooa.detooa.utils.bean.oa.request.OaMessageVo;
import com.detooa.detooa.utils.bean.oa.response.OaMessageResultVo;

import weaver.rsa.security.RSA;

/**
 * OA工具类
 * 
 * @apiNote 1.注册 2.获取token 3.发送消息
 */
public class OaUtils {

    public static void main(String[] args) throws Exception
    {
//        sendCustomMessage(new OaMessageVo("900495,900486,240051", "公交营运资源数据分析",
//                "看板地址：http://10.10.10.205/link/9UtBIQye \r\n 访问密码: qbTL",
//                "http://10.10.10.205/link/9UtBIQye","http://10.10.10.205/link/9UtBIQye"));
        sendCustomMessage(new OaMessageVo("900495", "公交营运资源数据分析", "看板地址访问密码", "http://10.10.10.205/link/9UtBIQye"));
    }

    public static Map<String, Object> REGIST_DATAS;// 注册缓存

    // token过期时间(单位：秒，不传默认1800秒，也就是30分钟过期)
    // {"msg":"获取成功!","code":0,"msgShowType":"none","status":true,"token":"bcda87c8-7fb8-4b10-9236-2b1139145443"}
    public static Map<String, Object> TOKEN_DATAS;// token缓存

    public static OaMessageResultVo sendCustomMessage(OaMessageVo messageVo) throws KeyManagementException, NoSuchAlgorithmException, IOException
    {
        // 判断消息
        if (null == messageVo) {
            throw new ServiceException("消息不能为空");
        }
        if (StringUtils.isEmpty(messageVo.getCode())) {
            messageVo.setCode(OaConfig.CODE);// 输入默认
        }
        if (StringUtils.isEmpty(messageVo.getLoginIdList())) {
            throw new ServiceException("接收人登录名不能为空");
        }
        if (StringUtils.isEmpty(messageVo.getCreater())) {
            messageVo.setCreater("1");
        }
        if (StringUtils.isEmpty(messageVo.getTitle())) {
            throw new ServiceException("标题不能为空");
        }
        if (StringUtils.isEmpty(messageVo.getContext())) {
            throw new ServiceException("内容不能为空");
        }
        if (StringUtils.isEmpty(messageVo.getLinkUrl())) {
            throw new ServiceException("PC端链接不能为空");
        }
        // 判断是否注册
        String spk = RSA.getRSA_PUB();
        if (StringUtils.isEmpty(spk)) {
            Map<String, Object> datas;
            try {
                datas = regist();
                spk = (String) (datas.get("spk"));
            } catch (KeyManagementException | NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
        }

        Map<String, String> heads = new HashMap<>();
        // 封装参数到请求头
        heads.put("appid", OaConfig.APPID);
        heads.put("token", getToken());
        heads.put("skipsession", "1");
        heads.put("Context-Type", "application/x-www-form-urlencoded; charset=utf-8");
        // 发送消息
        String data = post(OaConfig.BASE_URL + "/api/ec/dev/message/sendCustomMessageSingle", messageVo.toParams(), heads);
        System.out.println("testRestful：" + data);
        OaMessageResultVo messageResultVo = JSONObject.parseObject(data, OaMessageResultVo.class);
        return messageResultVo;
    }

    /**
     * 注册第三方系统到OA系统，第一次用APPID获取到公钥spk和私钥secret，第三方系统可以保存，下次不用在请求注册接口
     *
     * @param address OA地址
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     * @throws IOException 
     */
    public static Map<String, Object> regist() throws NoSuchAlgorithmException, KeyManagementException, IOException
    {
        Map<String, String> heads = new HashMap<>();
        // 获取当前异构系统RSA加密的公钥
//        String cpk = new RSA().getRSA_PUB();
        String cpk = RSA.getRSA_PUB();
        // 封装参数到请求头
        heads.put("appid", OaConfig.APPID);
        heads.put("cpk", cpk);
        // 调用ECOLOGY系统接口进行注册
        String data = post(OaConfig.BASE_URL + "/api/ec/dev/auth/regist", null, heads);
        return JSONObject.parseObject(data, new TypeReference<Map<String, Object>>() {
        });
    }

    public static Map<String, Object> applytoken() throws KeyManagementException, NoSuchAlgorithmException, IOException
    {
        Map<String, String> heads = new HashMap<>();
        // ECOLOGY返回的系统公钥
        RSA rsa = new RSA();
        // 对秘钥进行加密传输，防止篡改数据
        String secret = rsa.encrypt(null, getSecrit(), null, "utf-8", getSpk(), false);
        // 封装参数到请求头
        heads.put("appid", OaConfig.APPID);
        heads.put("secret", secret);
        heads.put("time", OaConfig.EXPIRE_TIME + "");
        // 调用ECOLOGY系统接口进行注册
        String data = post(OaConfig.BASE_URL + "/api/ec/dev/auth/applytoken", null, heads);
        System.out.println("=====testGetoken=====" + data);
        return JSONObject.parseObject(data, new TypeReference<Map<String, Object>>() {
        });
    }

    /**
     * 此方法仅供参考，第三方系统可采用自己的方式调用http接口
     *
     * @param path 请求路径
     * @param data 请求参数
     * @return
     * @throws IOException 
     */
    public static String post(String path, Map<String, String> params, Map<String, String> heads) throws IOException
    {
        String str = "";
        URL url = new URL(path);
        // 打开和url之间的连接
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//            PrintWriter out = null;
        // 请求参数 编码为 utf-8
        // 请求方式
        conn.setRequestMethod("POST");
        // 设置通用的请求属性
        conn.setRequestProperty("accept", "*/*");
        conn.setRequestProperty("connection", "Keep-Alive");
        conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");

        if (heads != null)
            for (Map.Entry<String, String> entry : heads.entrySet())
                conn.setRequestProperty(entry.getKey(), entry.getValue());

        // 设置是否向httpUrlConnection输出，设置是否从httpUrlConnection读入，此外发送post请求必须设置这两个
        // 最常用的Http请求无非是get和post，get请求可以获取静态页面，也可以把参数放在URL字串后面，传递给servlet，
        // post与get的 不同之处在于post的参数不是放在URL字串里面，而是放在http请求的正文内。
        conn.setDoOutput(true);
        conn.setDoInput(true);
        OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream(), "utf-8");

        if (params != null)
            out.write(mapToStr(params));
        // 缓冲数据
        out.flush();

        out.close();

        // 获取URLConnection对象对应的输入流
        InputStream is = conn.getInputStream();
        // 构造一个字符流缓存
        BufferedReader br = new BufferedReader(new InputStreamReader(is, "utf-8"));

        String result = "";
        while ((str = br.readLine()) != null) {
            result = str;
        }
        // 关闭流
        is.close();
        // 断开连接，最好写上，disconnect是在底层tcp socket链接空闲时才切断。如果正在被其他线程使用就不切断。
        // 固定多线程的话，如果不disconnect，链接会增多，直到收发不出信息。写上disconnect后正常一些。
        conn.disconnect();
        return result;
    }

    /**
     * 将Map转换成字符串参数，用于POST GET 请求
     *
     * @param map
     * @return
     */
    public static String mapToStr(Map<String, String> map)
    {
        StringBuilder stringBuilder = new StringBuilder();
        if (map != null) {
            for (Map.Entry<String, String> entry : map.entrySet()) {
                stringBuilder.append(entry.getKey());
                if (entry.getValue() != null)
                    stringBuilder.append("=").append(entry.getValue());
                stringBuilder.append("&");
            }
        }
        if (stringBuilder.length() > 0)
            return stringBuilder.substring(0, stringBuilder.length() - 1);
        return null;
    }

    public static String getSpk() throws KeyManagementException, NoSuchAlgorithmException, IOException
    {
        if (null == REGIST_DATAS) {
            REGIST_DATAS = regist();
        }
        return (String) (REGIST_DATAS.get("spk"));
    }

    public static String getSecrit() throws KeyManagementException, NoSuchAlgorithmException, IOException
    {
        if (null == REGIST_DATAS) {
            REGIST_DATAS = regist();
        }
        return (String) (REGIST_DATAS.get("secrit"));
    }

    public static String getToken() throws KeyManagementException, NoSuchAlgorithmException, IOException
    {
        if (null == TOKEN_DATAS || null == TOKEN_DATAS.get("tokenTime")) {
            TOKEN_DATAS = applytoken();
            TOKEN_DATAS.put("tokenTime", System.currentTimeMillis());
        } else {
            long tokenTime = (Long) TOKEN_DATAS.get("tokenTime");
            // token过期重新获取
            if (System.currentTimeMillis() - tokenTime > OaConfig.EXPIRE_TIME) {
                TOKEN_DATAS = applytoken();
            }
        }

        return (String) TOKEN_DATAS.get("token");
    }
}
